<?php
namespace App\Models\System;

use App\Exceptions\ApiException;
use App\Providers\LengthAwarePaginatorProvider;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use App\Enums\System\SysMenu\{IsFrame, IsCache, Visible, Status};

/**
* 菜单权限表 模型
* @create 2023-08-21 14:34:39
*/
class SysMenuModel extends \App\Models\BaseModel {
    use HasFactory;

    const Table         = "sys_menu";
    const PrimaryKey    = "menu_id";
    public $timestamps  = true;

    static $Wheres = ['menu_id','is_frame','is_cache','visible','status'];     // 直接定义column, 默认where(column, '=', value)
    public function __construct($attributes = []) {
        parent::__construct($attributes);

        // static::$Wheres['menu_id']   = fn($q, $v) => $q->where(atn('menu_id'), '=', $v);
        static::$Wheres['menu_name']   = fn($q, $v) => $q->where(atn('menu_name'), 'like', "%{$v}%");
    }

    /** xxxx */
    // public function user() {
        // return $this->hasOne(User::class, 'id', 'uid');
    // }

    /** ========== 常用查询 ========== */
    // 'parent_name',
    static $Vo      = ['menu_id','menu_name','parent_id','order_num','path','component','query','is_frame','is_cache','menu_type','visible','status','perms','icon','create_by','create_time','update_time','update_by','remark',];
    static $Voi     = ['*', ];

    // 菜单VO
    static $VoMenu  = ['menu_id','parent_id','menu_name','path','component','query','visible','status','perms','is_frame','is_cache','menu_type','icon','order_num','create_time',];

    /**
     * 查询系统菜单列表
     * 
     * @param $menu 菜单信息
     * @return SysMenuModel[]|Collection 菜单列表
     */
    public function selectMenuList(array $menu=null): Collection {
        return $this->selectVo()
            ->wheres($menu, 'menu_name', 'visible', 'status')
            ->orderBy('parent_id')
            ->orderBy('order_num')
            ->get()
            ;
    }

    /**
     * 根据用户所有权限
     * 
     * @return string[]|array 权限列表
     */
    public function selectMenuPerms(): array {
        return $this
            ->distinct()
            ->select(atn('perms'))
            ->leftJoin(SysRoleMenuModel::Table, SysRoleMenuModel::atn('menu_id'), '=', atn('menu_id'))
            ->leftJoin(SysUserRoleModel::Table, SysUserRoleModel::atn('role_id'), '=', SysRoleMenuModel::atn('role_id'))
            ->get()
            ->toArray()
            ;
    }

    /**
     * 根据用户查询系统菜单列表
     * 
     * @param $menu 菜单信息
     * @return SysMenuModel[]|Collection 菜单列表
     */
    public function selectMenuListByUserId(array $menu): Collection {
        return $this
            ->distinct()
            ->select(atn(static::$VoMenu))
            ->leftJoin(SysRoleMenuModel::Table, SysRoleMenuModel::atn('menu_id'), '=', atn('menu_id'))
            ->leftJoin(SysUserRoleModel::Table, SysUserRoleModel::atn('role_id'), '=', SysRoleMenuModel::atn('role_id'))
            ->leftJoin(SysRoleModel::Table, SysRoleModel::atn('role_id'), '=', SysUserRoleModel::atn('role_id'))
            ->where(SysUserRoleModel::atn('user_id'), '=', $menu['userId'])
            ->wheres($menu, 'menu_name', 'visible', 'status')
            ->orderBy('parent_id')
            ->orderBy('order_num')
            ->get()
            ;
    }

    /**
     * 根据用户ID查询权限
     * 
     * @param $userId 用户ID
     * @return string[]|array 权限列表
     */
    public function selectMenuPermsByUserId($userId): array {
        return $this
            ->distinct()
            ->select(atn('perms'))
            ->leftJoin(SysRoleMenuModel::Table, SysRoleMenuModel::atn('menu_id'), '=', atn('menu_id'))
            ->leftJoin(SysUserRoleModel::Table, SysUserRoleModel::atn('role_id'), '=', SysRoleMenuModel::atn('role_id'))
            ->leftJoin(SysRoleModel::Table, SysRoleModel::atn('role_id'), '=', SysUserRoleModel::atn('role_id'))
            ->where(atn('status'), '=', '0')
            ->where(SysRoleModel::atn('status'), '=', '0')
            ->where(SysUserRoleModel::atn('user_id'), '=', $userId)
            ->get()
            ->toArray()
            ;
    }

    /**
     * 根据角色ID查询权限
     * 
     * @param $roleId 角色ID
     * @return string[]|array 权限列表
     */
    public function selectMenuPermsByRoleId($roleId): array {
        return $this
            ->distinct()
            ->select(atn('perms'))
            ->leftJoin(SysRoleMenuModel::Table, SysRoleMenuModel::atn('menu_id'), '=', atn('menu_id'))
            ->where(atn('status'), '=', '0')
            ->where(SysRoleMenuModel::atn('role_id'), '=', $roleId)
            ->get()
            ->map(fn($it)=>$it['perms'])
            ->toArray()
            ;
    }

    /**
     * 根据用户ID查询菜单
     * 
     * @return SysMenuModel[]|Collection 菜单列表
     */
    public function selectMenuTreeAll(): Collection {
        return $this
            ->distinct()
            ->select(atn(static::$VoMenu))
            ->where('status', '=', '0')
            ->whereIn('menu_type', ['M', 'C'])
            ->orderBy('parent_id')
            ->orderBy('order_num')
            ->get()
            ;
    }

    /**
     * 根据用户ID查询菜单
     * 
     * @param $username 用户ID
     * @return SysMenuModel[]|Collection 菜单列表
     */
    public function selectMenuTreeByUserId(int $userId): Collection {
        return $this
            ->distinct()
            ->select(atn(static::$VoMenu))
            ->leftJoin(SysRoleMenuModel::Table, SysRoleMenuModel::atn('menu_id'), '=', atn('menu_id'))
            ->leftJoin(SysUserRoleModel::Table, SysUserRoleModel::atn('role_id'), '=', SysRoleMenuModel::atn('role_id'))
            ->leftJoin(SysRoleModel::Table, SysRoleModel::atn('role_id'), '=', SysUserRoleModel::atn('role_id'))
            ->leftJoin(SysUserModel::Table, SysUserModel::atn('user_id'), '=', SysUserRoleModel::atn('user_id'))
            ->where(atn('status'), '=', '0')
            ->whereIn(atn('menu_type'), ['M', 'C'])
            ->where(SysRoleModel::atn('status'), '=', '0')
            ->where(SysUserModel::atn('user_id'), '=', $userId)
            ->orderBy(atn('parent_id'))
            ->orderBy(atn('order_num'))
            ->get()
            ;
    }

    /**
     * 根据角色ID查询菜单树信息
     * 
     * @param $roleId 角色ID
     * @param $menuCheckStrictly 菜单树选择项是否关联显示
     * @return int[]|array 选中菜单列表
     */
    public function selectMenuListByRoleId(int $roleId, bool $menuCheckStrictly): array {
        return $this
            ->select(atn('menu_id'))
            ->leftJoin(SysRoleMenuModel::Table, SysRoleMenuModel::atn('menu_id'), '=', atn('menu_id'))
            ->where(SysRoleMenuModel::atn('role_id'), '=', $roleId)
            ->when($menuCheckStrictly, fn($q)=>$q->whereRaw(atn('menu_id') . " not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = $roleId)"))
            ->orderBy('parent_id')
            ->orderBy('order_num')
            ->get()
            ->toArray()
            ;
    }

    /**
     * 根据菜单ID查询信息
     * 
     * @param $menuId 菜单ID
     * @return SysMenuModel 菜单信息
     */
    public function selectMenuById(int $menuId): SysMenuModel {
        return throw_str(
            $this->selectVo()
                ->where(atn('menu_id'), '=', $menuId)
                ->first()
            ?? "菜单($menuId)查找失败");
    }

    /**
     * 是否存在菜单子节点
     * 
     * @param $menuId 菜单ID
     * @return int 结果
     */
    public function hasChildByMenuId($menuId): int {
        return $this
            ->select(static::PrimaryKey)
            ->where('parent_id', '=', $menuId)
            ->count()
            ;
    }

    /**
     * 新增菜单信息
     * 
     * @param $menu 菜单信息
     * @return SysMenuModel 结果
     */
    public function insertMenu(array $menu): SysMenuModel {
        $values = static::when(
                    $menu,
                    'menu_id',
                    'parent_id',
                    'menu_name',
                    'order_num',
                    'path',
                    'component',
                    'query',
                    'is_frame',
                    'is_cache',
                    'menu_type',
                    'visible',
                    'status',
                    'perms',
                    'icon',
                    'remark',
                    'create_by',
                );
        return throw_str(
            static::row_add($values)
        );
    }

    /**
     * 修改菜单信息
     * 
     * @param $menu 菜单信息
     * @return 结果
     */
    public function updateMenu($menu): int {
        $values = static::when(
                    $menu,
                    'parent_id',
                    'menu_name',
                    'order_num',
                    'path',
                    'component',
                    'query',
                    'is_frame',
                    'is_cache',
                    'menu_type',
                    'visible',
                    'status',
                    'perms',
                    'icon',
                    'remark',
                    'update_by',
                );
        return $this
                ->where(atn('menu_id'), '=', get_value($menu, 'menu_id'))
                ->update($values);
    }

    /**
     * 删除菜单管理信息
     * 
     * @param $menuId 菜单ID
     * @return 结果
     */
    public function deleteMenuById($menuId): int {
        return $this
                ->where('menu_id', '=', $menuId)
                ->delete()
                ;
    }

    /**
     * 校验菜单名称是否唯一
     * 
     * @param $menuName 菜单名称
     * @param $parentId 父菜单ID
     * @return SysMenuModel 结果
     */
    public function checkMenuNameUnique(string $menuName, int $parentId): SysMenuModel {
        return throw_str(
            $this->selectVo()
                ->where(atn('menu_name'), '=', $menuName)
                ->where(atn('parent_id'), '=', $parentId)
                ->first()
            ?? "菜单($menuName)查找失败");
    }

    /** 根据MenuId获取 */
    public function selectByMenuId($menu_id): SysMenuModel {
        $where = [['menu_id', '=', "{$menu_id}"]];
        return throw_str(
            static::row($where) ?? "数据($menu_id)为空~"
        );
    }


    /** 获取列表 @return SysMenu[]+paginator() */
    public function selectList(int|array $pag=1, array $param=[]): LengthAwarePaginatorProvider {
        return static::rows_pag($pag, [], fn($q)=>$q
                    // ->when(isset($param['menu_id']), fn($q)=>$q->where('menu_id', '=', $param['menu_id']))
                    ->wheres($param, 'menu_id','is_frame','is_cache','visible','status')
                    ->orderByDesc("menu_id")
        );
    }


    /** 插入数据 */
    public function insertData(array $data): SysMenuModel {
        return throw_str(
            static::row_add([
                // 'menu_id'        => $data['menu_id'],
                'menu_name'         => $data['menu_name'],
                'parent_id'         => $data['parent_id'] ?? '0',
                'order_num'         => $data['order_num'] ?? '0',
                'path'              => $data['path'] ?? '',
                'component'         => $data['component'] ?? '',
                'query'             => $data['query'] ?? '',
                'is_frame'          => $data['is_frame'] ?? '1',
                'is_cache'          => $data['is_cache'] ?? '0',
                'menu_type'         => $data['menu_type'] ?? '',
                'visible'           => $data['visible'] ?? '0',
                'status'            => $data['status'] ?? '0',
                'perms'             => $data['perms'] ?? '',
                'icon'              => $data['icon'] ?? '#',
                'create_by'         => $data['create_by'] ?? '',
                // 'create_time'    => $data['create_time'] ?? '',
                'update_by'         => $data['update_by'] ?? '',
                // 'update_time'    => $data['update_time'] ?? '',
                'remark'            => $data['remark'] ?? '',
            ])
        );
    }


    /** 更新数据 */
    public function updateData(int|array $idOrWhere, array $data): SysMenuModel {
        $values = static::when(
                    $data, 
                    // 'menu_id',
                    'menu_name',
                    'parent_id',
                    'order_num',
                    'path',
                    'component',
                    'query',
                    'is_frame',
                    'is_cache',
                    'menu_type',
                    'visible',
                    'status',
                    'perms',
                    'icon',
                    'create_by',
                    // 'create_time',
                    'update_by',
                    // 'update_time',
                    'remark',
                );
        return throw_str(
            static::row_update($idOrWhere, $values)
        );
    }


    /** 删除数据 */
    public function deleteData(int|array $idOrWhere): SysMenuModel {
        return throw_str(
            static::row_delete($idOrWhere)
        );
    }




}